#include "ScnsTm1638.c.h"
#include "ScnsTimer.h"

#if defined(SCNS_TM1638_ENABLE)&&SCNS_TM1638_ENABLE==1

//@scnsBsp:https://gitee.com/huoxingdawang/scnsDemos-MM32F3277_seekfree/tree/master/Libraries/scnsBsp/MM32F3277_seekfree/

static struct
{
    uint8 inited;
#if defined(SCNS_KEY_ENABLE)&&SCNS_KEY_ENABLE==1
    uint8 status[16];
#endif
#if defined(SCNS_BSP_TM1638_CACHE)&&SCNS_BSP_TM1638_CACHE==1
    uint8 bright;
    uint8 display[8];
#endif
}rd[SCNS_TM1638_MAX];

#if defined(SCNS_BSP_TM1638_DELAY_TICK)&&SCNS_BSP_TM1638_DELAY_TICK!=0
#define delay()     do{for(volatile uint16 k=0;k<SCNS_BSP_TM1638_DELAY_TICK;++k);}while(0)
#else
#define delay()     do{}while(0)
#endif
#define dioDir(x)   do{scnsBspTm1638SetDioDir(tm1N,x);}while(0)
#define dio(x)      do{scnsBspTm1638SetDio(tm1N,x);}while(0)
#define dioGet()    scnsBspTm1638GetDio(tm1N)
#define clk(x)      do{scnsBspTm1638SetClk(tm1N,x);}while(0)
#define stb(x)      do{scnsBspTm1638SetStb(tm1N,x);}while(0)

static void WB(ScnsTm1638Enum tm1N,uint8 data)
{
    dioDir(0);
    for(uint8 i=0;i<8;++i)
    {
        clk(0);
        dio((data&0X01)?1:0);
        data>>=1;
        delay();
        clk(1);
        delay();
    }
}

static uint8 RB(ScnsTm1638Enum tm1N)
{
    unsigned char temp=0;
    dioDir(1);
    for(uint8 i=0;i<8;++i)
    {
        temp>>=1;
        clk(0);
        delay();
        temp|=dioGet()?0x80:0;
        clk(1);
        delay();
    }
    return temp;
}

static void WC(ScnsTm1638Enum tm1N,uint8 cmd)
{
    stb(0);
    WB(tm1N,cmd);
    stb(1);
}

//[0,7]
static void brightWrite(ScnsTm1638Enum tm1N,uint8 bri)
{
    WC(tm1N,0X88|bri);
}

static void displayWrite(ScnsTm1638Enum tm1N,const uint8 data[8])
{
    WC(tm1N,0X40);
    stb(0);
    WB(tm1N,0XC0);
    
    for(uint8 i=0;i<8;++i)
    {
        WB(tm1N,data[i]);
        WB(tm1N,0x00);
    }
    
    stb(1);
}

static void displayConvert(ScnsTm1638Enum tm1N,const uint8 data[],uint8 out[],uint8 len)
{
    static const uint8 tab[17]={
        0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
        0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
        0x80
    };
    const uint8*t=scnsBspTm1638Config[tm1N].tab?scnsBspTm1638Config[tm1N].tab:tab;
    for(uint8 i=0;i<len;++i)
        out[i]=(data[i]==0XFF)?(0):(t[data[i]&0X0F]|((data[i]&0XF0)?t[16]:0));
}

static uint8 displayEncode(ScnsTm1638Enum tm1N,const uint8 data[],uint8 start,uint8 len,uint8 out[8])
{
    uint8 changed=0;
    for(uint8 i=0;i<8;++i)
    {
        uint8 backup=out[i];
        for(uint8 k=0;k<len;++k)
        {
            const uint8 j=start+k;
            out[i]&=~(1<<j);
            out[i]|=((data[k]>>i)&0X01)<<j;
        }
        changed|=(backup!=out[i]);
    }
    return changed?1:0;
}

ScnsTm1638Status scnsTm1638Init(ScnsTm1638Enum tm1N)
{
    SCNS_ASSERT_ON_RUN(tm1N<SCNS_TM1638_MAX);
    scnsCheck(ScnsTm1638Status,scnsBspTm1638Init(tm1N),SCNS_TM1638_STATUS_OK);
    brightWrite(tm1N,7);
    displayWrite(tm1N,(const uint8[]){0,0,0,0,0,0,0,0});
    scnsCheck(ScnsTm1638Status,scnsBspTm1638PostInit(tm1N),SCNS_TM1638_STATUS_OK);
    rd[tm1N].inited=1;
    return SCNS_TM1638_STATUS_OK;
}

void scnsTm1638Display(ScnsTm1638Enum tm1N,const uint8 data[8])
{
    SCNS_ASSERT_ON_RUN(tm1N<SCNS_TM1638_MAX);
    if(!rd[tm1N].inited)return;
    uint8 tmp1[8];
    displayConvert(tm1N,data,tmp1,8);
#if defined(SCNS_BSP_TM1638_CACHE)&&SCNS_BSP_TM1638_CACHE==1
    rd[tm1N].inited|=(displayEncode(tm1N,tmp1,0,8,rd[tm1N].display)<<2);
#else
    uint8 tmp2[8];
    displayEncode(tm1N,tmp1,0,8,tmp2);
    displayWrite(tm1N,tmp2);
#endif
}

void scnsTm1638DisplayPartial(ScnsTm1638Enum tm1N,const uint8 data[],uint8 start,uint8 len)
{
    SCNS_ASSERT_ON_RUN(tm1N<SCNS_TM1638_MAX);
    SCNS_ASSERT_ON_RUN(start+len<=8);
    if(!rd[tm1N].inited)return;
    uint8 tmp1[8];
    displayConvert(tm1N,data,tmp1,len);
#if defined(SCNS_BSP_TM1638_CACHE)&&SCNS_BSP_TM1638_CACHE==1
    rd[tm1N].inited|=(displayEncode(tm1N,tmp1,start,len,rd[tm1N].display)<<2);
#else
    uint8 tmp2[8]={0,0,0,0,0,0,0,0};
    displayEncode(tm1N,tmp1,start,len,tmp2);
    displayWrite(tm1N,tmp2);
#endif
}

void scnsTm1638SetBright(ScnsTm1638Enum tm1N,uint8 bright)
{
    SCNS_ASSERT_ON_RUN(tm1N<SCNS_TM1638_MAX);
    SCNS_ASSERT_ON_RUN(bright<=7);
    if(!rd[tm1N].inited)return;
#if defined(SCNS_BSP_TM1638_CACHE)&&SCNS_BSP_TM1638_CACHE==1
    rd[tm1N].inited|=(1<<1);
    rd[tm1N].bright=bright;
#else
    brightWrite(tm1N,bright);
#endif
}

void scnsTm1638Sync(ScnsTm1638Enum tm1N)
{
    SCNS_ASSERT_ON_RUN(tm1N<SCNS_TM1638_MAX);
    if(!rd[tm1N].inited)return;
#if defined(SCNS_BSP_TM1638_CACHE)&&SCNS_BSP_TM1638_CACHE==1
    if(rd[tm1N].inited&((1<<1)))
    {
        rd[tm1N].inited&=~((1<<1));
        brightWrite(tm1N,rd[tm1N].bright);
    }
    if(rd[tm1N].inited&((1<<2)))
    {
        rd[tm1N].inited&=~((1<<2));
        displayWrite(tm1N,rd[tm1N].display);
    }
#endif
#if defined(SCNS_KEY_ENABLE)&&SCNS_KEY_ENABLE==1
    static const uint8 keyTab[16][2]={
        {0,0x04},
        {0,0x40},
        {1,0x04},
        {1,0x40},
        {2,0x04},
        {2,0x40},
        {3,0x04},
        {3,0x40},
        {0,0x02},
        {0,0x20},
        {1,0x02},
        {1,0x20},
        {2,0x02},
        {2,0x20},
        {3,0x02},
        {3,0x20},
    };
    uint8 c[4];
    stb(0);
    WB(tm1N,0x42);
    for(uint8 i=0;i<4;i++)
        c[i]=RB(tm1N);
    stb(1);
    for(uint8 i=0;i<16;++i)
        scnsKeyStatusMachine(&rd[tm1N].status[i],c[keyTab[i][0]]==keyTab[i][1],SCNS_BSP_TM1638_KEY_DOWN_TICK,SCNS_BSP_TM1638_KEY_HOLD_TICK,SCNS_BSP_TM1638_KEY_HOLD_DOWN_TICK,scnsBspTm1638Config[tm1N].key[i]);
#endif
}

#endif
